﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Xml.Serialization;


namespace GraphicsEditor
{
    public enum FillType { Solid, Hatch };

    // serialization used for Copy and Paste does not support complex types such as Pen and Brush. Use simple types instead.
    [Serializable]
    public class ShapePen
    {
        [XmlAttribute("color")]
        public Color Color = Color.Black;
        [XmlAttribute("dashstyle")]
        public DashStyle DashStyle;
        [XmlAttribute("startcap")]
        public LineCap StartCap;
        [XmlAttribute("endcap")]
        public LineCap EndCap;
        [XmlAttribute("width")]
        public int Width;

        public Pen GetPen()
        {
            Pen pen = new Pen(new SolidBrush(Color), Width);
            pen.DashStyle = DashStyle;
            pen.StartCap = StartCap;
            pen.EndCap = EndCap;            
            return pen;
        }

        public ShapePen Clone()
        {
            ShapePen p = new ShapePen();
            p.Color = Color;
            p.DashStyle = DashStyle;
            p.StartCap = StartCap;
            p.EndCap = EndCap;
            p.Width = Width;
            return p;
        }

    }

    [Serializable]
    public class ShapeBrush
    {
        [XmlAttribute("filltype")]
        public FillType FillType = FillType.Solid;
        [XmlAttribute("forecolor")]
        public Color ForeColor = Color.White;
        [XmlAttribute("backcolor")]
        public Color BackColor =  Color.White;
        [XmlAttribute("hatchstyle")]
        public HatchStyle HatchStyle;

        public Brush GetBrush()
        {
            Brush brush = null;
            switch (FillType)
            {
                case FillType.Solid:
                    brush = new SolidBrush(ForeColor);
                    break;
                case FillType.Hatch:
                    brush = new HatchBrush(HatchStyle, ForeColor, BackColor);
                    break;
                default:
                    brush = new SolidBrush(ForeColor);
                    break;
            }
            return brush;
        }

        public ShapeBrush Clone()
        {
            ShapeBrush s = new ShapeBrush();
            s.FillType = FillType;
            s.ForeColor = ForeColor;
            s.BackColor = BackColor;
            s.HatchStyle = HatchStyle;
            return s;

        }

    }

    [Serializable]
    [XmlRoot("shape")]
    public abstract class Shape
    {

        private ShapePen pen;
        [XmlAttribute("pen")]
        public ShapePen Pen
        {
            get
            {
                return pen;
            }
            set
            {
                pen = value;
            }
        }

        private ShapeBrush brush;
        [XmlAttribute("brush")]
        public ShapeBrush Brush
        {
            get
            {
                return brush;
            }
            set
            {
                brush = value;
            }
        }


        protected List<Point> points = new List<Point>();
        
        [XmlAttribute("points")]
        public List<Point> Points {
            get { return points; }
            set { points = value; }
        }

        public int NumOfPoints
        {
            get { return points.Count; }
        }

        public Point this[int x]
        {
            get { return points[x]; }
            set
            {
                points[x] = value;
            }
        }

        public Shape(ShapePen pen, ShapeBrush brush)
        {
            this.pen = pen.Clone();
            this.brush = brush.Clone();
        }

        // Draw itself, decendants should override it.
        public abstract void Draw(Graphics g);
        public abstract bool IsEnd(Point p);

        // move last point
        public void RemoveAt(int index)
        {
            points.RemoveAt(index);
        }

        public void AddPoint(Point p)
        {
            points.Add(p);
        }

        public void Offset(int x, int y)
        {
            for (int i = 0; i < points.Count; i++)
            {
                Point p = points[i];
                p.Offset(x, y);
                points[i] = p;
            }
        }
        public abstract bool PtInShape(Point p);


        public Rectangle GetBoundingRect()
        {
            //find the min coordinate for each point of shape
            int xUL = Int32.MaxValue, yUL = Int32.MaxValue, xBR = 0, yBR = 0;

            foreach (Point p in points)
            {

                if (p.X < xUL)
                    xUL = p.X;
                if (p.Y < yUL)
                    yUL = p.Y;

                if (p.X > xBR)
                    xBR = p.X;
                if (p.Y > yBR)
                    yBR = p.Y;
            }

            return new Rectangle(xUL, yUL, xBR - xUL, yBR - yUL);
        }

        public Color FillColor { get; set; }
    }

    // A rectangle
    [Serializable]
    public class Line : Shape
    {
        public Line(ShapePen pen, ShapeBrush brush) : base(pen, brush) { }
        
        public override bool PtInShape(Point p)
        {
            if (NumOfPoints == 2)
            {
                Rectangle rect = GetBoundingRect();
                return p.X > rect.Left && p.X < rect.Right && p.Y > rect.Top && p.Y < rect.Bottom;
            }
            return false;
        }

        public override void Draw(Graphics g)
        {
            if (NumOfPoints == 2)
            {
                g.DrawLine(Pen.GetPen(), points[0].X, points[0].Y, points[NumOfPoints - 1].X, points[NumOfPoints - 1].Y);
            }
        }

        public override bool IsEnd(Point p)
        {
            return NumOfPoints == 2;

        }
    }



    // A rectangle
    [Serializable]
    public class Rect : Shape
    {
        public Rect(ShapePen pen, ShapeBrush brush) : base(pen, brush) { }
        public override bool PtInShape(Point p)
        {
            if (NumOfPoints == 2)
            {
                Rectangle rect = GetBoundingRect();
                return p.X > rect.Left && p.X < rect.Right && p.Y > rect.Top && p.Y < rect.Bottom;
            }
            return false;
        }

        public override void Draw(Graphics g)
        {
            if (NumOfPoints == 2)
            {
                g.FillRectangle(Brush.GetBrush(), GetBoundingRect());
                g.DrawRectangle(Pen.GetPen(), GetBoundingRect());
            }
        }

        public override bool IsEnd(Point p)
        {
            return NumOfPoints == 2;

        }
    }

    [Serializable]
    public class Ellipse : Shape
    {
        public Ellipse(ShapePen pen, ShapeBrush brush) : base(pen, brush) { }
       public override bool PtInShape(Point p)
        {
            if (NumOfPoints == 2)
            {
                Rectangle rect = GetBoundingRect();
                return p.X > rect.Left && p.X < rect.Right && p.Y > rect.Top && p.Y < rect.Bottom;
            }
            return false;
        }

        public override void Draw(Graphics g)
        {
            if (NumOfPoints == 2)
            {
                g.FillEllipse(Brush.GetBrush(), GetBoundingRect());
                g.DrawEllipse(Pen.GetPen(), GetBoundingRect());

            }
        }

        public override bool IsEnd(Point p)
        {
            return NumOfPoints == 2;

        }
    }

    [Serializable]
    public class Polygon : Shape
    {
        public Polygon(ShapePen pen, ShapeBrush brush) : base(pen, brush) { }
        public override bool PtInShape(Point p)
        {
            if (NumOfPoints >= 3)
            {
                Rectangle rect = GetBoundingRect();
                return p.X > rect.Left && p.X < rect.Right && p.Y > rect.Top && p.Y < rect.Bottom;
            }
            return false;
        }

        public override void Draw(Graphics g)
        {
            if (NumOfPoints >= 2)
            {
                g.FillPolygon(Brush.GetBrush(), points.ToArray());
                g.DrawPolygon(Pen.GetPen(), points.ToArray());
            }
        }

        public override bool IsEnd(Point p)
        {
            return Math.Abs(p.X - points[0].X) < 10 && Math.Abs(p.Y - points[0].Y) < 10;
        }

    }

    [Serializable]
    public class ShapeFile
    {
        public List<Shape> ShapeList {get; set;}
        public Size Size {get; set;}
    }

}
